package com.wqnmdb.im.netty.dispose.impl;

import com.mongodb.client.result.UpdateResult;
import com.wqnmdb.im.domain.contants.ConstantEnum;
import com.wqnmdb.im.domain.mongo.*;
import com.wqnmdb.im.netty.data.NettyData;
import com.wqnmdb.im.domain.netty.protobuf.Message;
import com.wqnmdb.im.service.mongo.MongoService;
import com.wqnmdb.im.service.redis.RedisStringService;
import com.wqnmdb.im.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;


/**
 * 异步操作及缓存数据处理服务
 */
@Slf4j
@Service
@CacheConfig(cacheNames = {"im:cache"})
public class AsynAndCacheService extends NettyData {

    @Autowired
    private MongoService mongoService;

    @Autowired
    private RedisStringService redisStringService;


    /**
     * 检查用户是否存在
     */
    @Cacheable(key = "methodName + ':' +#p0+ '+' +#p1", unless = "#result == false")
    public boolean checkUserExists(Integer userId, String appName, String collectionName) {
        boolean bool = false;
        try {
            long count = mongoService.count(new Query(
                            Criteria.where("userId").is(userId).and("appName").is(appName).and("status").is(1)),
                    ImUser.class, collectionName);
            if (count > 0) {
                bool = true;
            }
        } catch (Exception e) {
            log.error("检查用户-异常：" + e.getMessage(), e);
        } finally {
            return bool;
        }
    }

    /**
     * 缓存用户信息
     */
    @Cacheable(key = "methodName + ':' + #p0", unless = "#result == null")
    public ImUser getUser(Integer userId, String collectionName) {
        ImUser user = mongoService.findOne(
                new Query(Criteria.where("userId").is(userId).and("status").is(1)), ImUser.class, collectionName);
        return user;
    }

    /**
     * 缓存app信息
     */
    @Cacheable(key = "methodName + ':' + #p0", unless = "#result == null")
    public ImApp getApp(String appName) {
        ImApp app = mongoService.findOne(new Query(Criteria.where("name").is(appName).and("status").is(1)), ImApp.class);
        return app;
    }

    /**
     * 按照app拆分账户文档
     */
    @Cacheable(key = "methodName + ':' +#p0", unless = "#result == null")
    public String getUserCollectionName(String appName) {
        String collectionName = ConstantEnum.IM_USER.getContent();
        collectionName += "_";
        collectionName += appName;
        return collectionName;
    }

    /**
     * 按照app拆分消息文档
     */
    @Cacheable(key = "methodName + ':' +#p0", unless = "#result == null")
    public String getMsgCollectionName(String appName) {
        String collectionName = ConstantEnum.IM_MESSAGE.getContent();
        collectionName += "_";
        collectionName += appName;
        return collectionName;
    }

    /**
     * 按照app拆分消息文档
     */
    @Cacheable(key = "methodName + ':' +#p0", unless = "#result == null")
    public String getAffirmCollectionName(String appName) {
        String collectionName = ConstantEnum.IM_AFFIRM.getContent();
        collectionName += "_";
        collectionName += appName;
        return collectionName;
    }

    /**
     * 按照app拆分消息文档
     */
    @Cacheable(key = "methodName + ':' +#p0", unless = "#result == null")
    public String getSessionCollectionName(String appName) {
        String collectionName = ConstantEnum.IM_SESSION.getContent();
        collectionName += "_";
        collectionName += appName;
        return collectionName;
    }

    /**
     * 获取SessionID
     */
    @Cacheable(key = "methodName + ':' +#p0+ '+' +#p1+ '+' +#p2", unless = "#result == null")
    public String getSessionId(Integer fromUserId, Integer toUserId, String appName, String collectionNameS) {
        String sessionId = null;
        //查询是否存在会话
        Criteria criteria = new Criteria();
        criteria.orOperator(
                Criteria.where("originUserId").is(fromUserId).and("type").is(1)
                        .and("particUserId").is(toUserId).and("status").is(1),
                Criteria.where("particUserId").is(fromUserId).and("type").is(1)
                        .and("originUserId").is(toUserId).and("status").is(1));
        ImSession session = mongoService.findOne(new Query(criteria), ImSession.class, collectionNameS);
        if (session == null) {
            //创建session
            ImSession imSession = new ImSession();
            imSession.setAppName(appName);
            imSession.setType(1);
            imSession.setStatus(1);
            imSession.setOriginUserId(fromUserId);
            imSession.setParticUserId(toUserId);
            imSession.setCreateTime(DateUtil.getNowInt());
            imSession.setUpdateTime(DateUtil.getNowInt());
            ImSession insert = mongoService.insert(imSession, collectionNameS);
            sessionId = insert.getId();
        } else {
            Update update = new Update();
            if (session.getOriginUserDel() == 1 || session.getParticUserDel() == 1) {
                update.set("originUserDel", 0);
                update.set("particUserDel", 0);
                update.set("updateTime", DateUtil.getNowInt());
                mongoService.updateFirst(new Query(Criteria.where("id").is(session.getId())), update, ImSession.class, collectionNameS);
            }
            sessionId = session.getId();
        }
        return sessionId;
    }

    /**
     * 检查session
     */
    @Cacheable(key = "methodName + ':' +#p1+ '+' +#p2+ '+' +#p0", unless = "#result == false")
    public boolean checkSessionId(String sessionId, Integer fromUserId, Integer toUserId, String collectionName) {
        ImSession session = mongoService.findById(sessionId, ImSession.class, collectionName);
        if (session != null){
            if (session.getParticUserId().equals(fromUserId) && session.getOriginUserId().equals(toUserId)){
                return true;
            }
            if (session.getParticUserId().equals(toUserId) && session.getOriginUserId().equals(fromUserId)){
                return true;
            }
        }
        return false;
    }


    /**
     * 删除缓存
     */
    @Async
    public void delCache(Integer userId, String appName) {
        log.info("清除账号缓存，userId：{}.", userId);
        String userCollectionName = this.getUserCollectionName(appName);
        ImUser user = mongoService.findOne(new Query(
                        Criteria.where("userId").is(userId).and("appName").is(appName)),
                ImUser.class, userCollectionName);
        if (user != null) {
            String key1 = "im:cache::getUser:" + userId;
            String key2 = "im:cache::checkUserExists:" + userId + "+" + appName;
            redisStringService.delKey(key1);
            redisStringService.delKey(key2);
        }
    }

    /**
     * 删除SESSION缓存
     */
    @Async
    public void delSessionCache(ImSession session, String appName) {
        String key1 = "im:cache::getSessionId:";
        String key2 = "im:cache::getSessionId:";
        String key3 = "im:cache::checkSessionId:";
        String key4 = "im:cache::checkSessionId:";
        //删缓存
        key1 += session.getOriginUserId() + "+" + session.getParticUserId() + "+" + appName;
        key2 += session.getOriginUserId() + "+" + session.getParticUserId() + "+" + appName;
        key3 += session.getOriginUserId() + "+" + session.getParticUserId() + "+" + "*";
        key4 += session.getOriginUserId() + "+" + session.getParticUserId() + "+" + "*";
        redisStringService.delKey(key1);
        redisStringService.delKey(key2);
        redisStringService.delKeyByPrefix(key3);
        redisStringService.delKeyByPrefix(key4);
        log.info("清除session缓存完成");
    }

    /**
     * 存储消息数据
     */
    @Async
    public void insertMsg(Message.MessageInfo msgInfo, String appName, String collectionName) {
        String sessionId = msgInfo.getSessionId();
        //存消息
        long count = mongoService.count(new Query(Criteria.where("uuid").is(msgInfo.getUuid())
                        .and("sessionId").is(sessionId).and("appName").is(appName)),
                ImMessage.class, collectionName);
        if (count == 0) {
            ImMessage imMsg = new ImMessage();
            BeanUtils.copyProperties(msgInfo, imMsg);
            imMsg.setAppName(appName);
            imMsg.setSessionId(sessionId);
            imMsg.setCreateTime(DateUtil.getNowInt());
            imMsg.setUpdateTime(DateUtil.getNowInt());
            mongoService.insert(imMsg, collectionName);
        }
    }

    /**
     * 消息送达回执  status  -1已撤回  1-已送达  2-已读
     */
    @Async
    public void updateMsg(String uuid, String sessionId, String appName, int status, String collectionName) {
        Query query = new Query(Criteria.where("uuid").is(uuid).and("sessionId").is(sessionId).and("appName").is(appName));

        UpdateResult updateResult = mongoService.updateFirst(
                query, new Update().set("status", status).set("updateTime", DateUtil.getNowInt()),
                ImMessage.class, collectionName);
        log.info("回执消息ID:{}.变更结果:{}.", uuid, updateResult.getModifiedCount());
    }

    /**
     * app在线人数及用户在线状态
     * type 1-app人数加用户上线  2-app人数减用户下线
     */
    @Async
    public void statAppAndUser(String appName, Integer userId, int type) {
        int onLineInc = type == 1 ? 1 : -1;
        int isOnLine = type == 1 ? 1 : 0;

        UpdateResult updateApp = mongoService.updateFirst(
                new Query(Criteria.where("name").is(appName).and("status").is(1)),
                new Update().inc("onLineNum", onLineInc).set("updateTime", DateUtil.getNowInt()), ImApp.class);
        log.info("appName:{}.添加在线人数结果：{}.", appName, updateApp.getModifiedCount());

        UpdateResult updateUser = mongoService.updateFirst(
                new Query(Criteria.where("userId").is(userId).and("status").is(1)),
                new Update().set("isOnLine", isOnLine).set("updateTime", DateUtil.getNowInt()),
                ImUser.class, getUserCollectionName(appName));
        log.info("appName:{}.用户:{}.修改上线状态结果：{}.", appName, userId, updateUser.getModifiedCount());
    }

    /**
     * 接收方不在线，将确认消息保存
     */
    @Async
    public void insertMsgAffirm(Integer userId,
                                String appName, String sessionId, String uuid, int status){
        ImAffirm imAffirm = new ImAffirm();
        imAffirm.setReceiveUserId(userId);
        imAffirm.setAppName(appName);
        imAffirm.setSessionId(sessionId);
        imAffirm.setUuid(uuid);
        imAffirm.setStatus(status);
        imAffirm.setCreateTime(DateUtil.getNowInt());
        mongoService.insert(imAffirm, getAffirmCollectionName(appName));
    }

    /**
     * 删除确认消息
     */
    @Async
    public void delMsgAffirm(String id, String appName){
        mongoService.remove(new Query(Criteria.where("id").is(id)), ImAffirm.class, getAffirmCollectionName(appName));
    }
}


